package org.interfaceadaptation.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import org.interfaceadaptation.InterfaceAdaptationError;
import org.interfaceadaptation.parameter.expression.Expression;

public class ReflectionUtils {

	private ReflectionUtils() {

	}

	public static <E> E newInstance(Class<E> clazz) {
		try {
			return clazz.newInstance();
		} catch (InstantiationException e) {
			throw new InterfaceAdaptationError("Could not create instannce of "
					+ clazz.getName() + " because " + e, e);
		} catch (IllegalAccessException e) {
			throw new InterfaceAdaptationError("Could not create instannce of "
					+ clazz.getName() + " because " + e, e);
		}
	}

	public static Object invoke(Method method, Object target, Object... args) {
		try {
			return method.invoke(target, args);
		} catch (IllegalArgumentException e) {
			throw new InterfaceAdaptationError("Could not call method "
					+ method + " because " + e, e);
		} catch (IllegalAccessException e) {
			throw new InterfaceAdaptationError("Could not call method "
					+ method + " because " + e, e);
		} catch (InvocationTargetException e) {
			throw new InterfaceAdaptationError("Could not call method "
					+ method + " because " + e, e);
		}
	}

	public static Object invokePropertyGetter(
			PropertyDescriptor propertyDescriptor, Object target) {
		return invoke(propertyDescriptor.getReadMethod(), target);
	}

	public static void invokePropertySetter(
			PropertyDescriptor propertyDescriptor, Object target, Object value) {
		invoke(propertyDescriptor.getWriteMethod(), target, value);
	}

	public static PropertyDescriptor getPropertyByName(
			Expression expression, String propertyName) {
		BeanInfo beanInfo = getBeanInfo(expression.getReturnType());
		return getPropertyDescriptor(beanInfo, propertyName);
	}

	public static BeanInfo getBeanInfo(Class<?> clazz) {
		try {
			return java.beans.Introspector.getBeanInfo(clazz);
		} catch (IntrospectionException e) {
			throw new InterfaceAdaptationError("introspection error:" + e, e);
		}
	}

	public static PropertyDescriptor getPropertyDescriptor(BeanInfo beanInfo,
			String propertyName) {
		for (PropertyDescriptor propertyDescriptor : beanInfo
				.getPropertyDescriptors()) {
			if (propertyDescriptor.getName().equals(propertyName)) {
				return propertyDescriptor;
			}
		}
		throw new InterfaceAdaptationError("Could not find property"
				+ propertyName + " for bean class "
				+ beanInfo.getBeanDescriptor().getBeanClass().getName());

	}

	public static boolean hasStaticValueOf(Class<?> clazz, Class<?> paramType) {
		return getStaticValueOf(clazz, paramType) != null;
	}

	public static Method getStaticValueOf(Class<?> clazz, Class<?> paramType) {
		    Method valueOfMethod = getMethod(clazz,"valueOf", paramType);
            if (Modifier.isStatic(valueOfMethod.getModifiers())) {
                return valueOfMethod;
            } else {
                return null;
            }
    }

	public static Method getMethod(Class<?> clazz, String methodName,
			Class<?>... paramTypes) {
		try {
			return clazz.getMethod(methodName, paramTypes);
		} catch (SecurityException e) {
			throw new InterfaceAdaptationError(
					"Could not find static valueOf method for class "
							+ clazz.getName() + " beacuse " + e, e);
		} catch (NoSuchMethodException e) {
			throw new InterfaceAdaptationError(
					"Could not find static valueOf method for class "
							+ clazz.getName() + " beacuse " + e, e);
		}
	}

}
